package jp.co.nes.awssupport.business.utility;

import java.math.BigDecimal;
import java.math.RoundingMode;

import jp.co.nes.awssupport.business.bean.BillingCsvBean;
import jp.co.nes.awssupport.common.base.CommonException;
import jp.co.nes.awssupport.common.bean.ContractInformationBean;

/**
 * レートヘルパーです。
 */
public class RateHelper {

    /** レート */
    private BigDecimal rate;

    /** 使用量 */
    private BigDecimal usageQuantity;

    /**
     * デフォルトコンストラクタです。
     */
    public RateHelper() {
    }

    /**
     * レートを返却します。
     *
     * @return レート
     */
    public BigDecimal getRate() {
        return rate;
    }

    /**
     * 使用量を返却します。<br>
     * レート計算時に、使用量の変換が必要だった場合にのみ値が設定されます。
     *
     * @return 使用量
     */
    public BigDecimal getUsageQuantity() {
        return usageQuantity;
    }

    /**
     * リザーブド購入(前払い料金)に関するレートを計算します。
     *
     * @param csvBean 請求CSV
     * @param ciBean 契約情報
     */
    public void calcForReservedUpfront(BillingCsvBean csvBean, ContractInformationBean ciBean) {
        clear();
        calc(csvBean, ciBean, false);
    }

    /**
     * リザーブド購入(定額月額料金)に関するレートを計算します。
     *
     * @param csvBean 請求CSV
     * @param ciBean 契約情報
     */
    public void calcForReservedMonthlyFixed(BillingCsvBean csvBean, ContractInformationBean ciBean) {
        clear();
        // スポット、リザーブド購入(前払い料金)以外でも、非混合レートがnullだった場合は、非混合料金と使用量で算出する。
        calc(csvBean, ciBean, false);
    }

    /**
     * リザーブドインスタンスの従量料金に関するレートを計算します。
     *
     * @param csvBean 請求CSV
     * @param ciBean 契約情報
     */
    public void calcForReservedMetered(BillingCsvBean csvBean, ContractInformationBean ciBean) {
        clear();
        // スポット、リザーブド購入(前払い料金)以外でも、非混合レートがnullだった場合は、非混合料金と使用量で算出する。
        calc(csvBean, ciBean, false);
    }

    /**
     * 融通リザーブドに関するレートを計算します。
     *
     * @param csvBean 請求CSV
     * @param ciBean 契約情報
     */
    public void calcForBlendedReserved(BillingCsvBean csvBean, ContractInformationBean ciBean) {
        clear();
        // スポット、リザーブド購入(前払い料金)以外でも、パブリックオンデマンドレートがnullだった場合は、
        // パブリックオンデマンドコストと使用量で算出する。
        calc(csvBean, ciBean, true);
    }

    /**
     * スポットに関するレートを計算します。
     *
     * @param csvBean 請求CSV
     * @param ciBean 契約情報
     */
    public void calcForSpot(BillingCsvBean csvBean, ContractInformationBean ciBean) {
        clear();
        calc(csvBean, ciBean, false);
    }

    /**
     * オンデマンドに関するレートを計算します。
     *
     * @param csvBean 請求CSV
     * @param ciBean 契約情報
     */
    public void calcForOnDemand(BillingCsvBean csvBean, ContractInformationBean ciBean) {
        clear();
        // スポット、リザーブド購入(前払い料金)以外でも、非混合レートがnullだった場合は、非混合料金と使用量で算出する。
        calc(csvBean, ciBean, false);
    }

    /**
     * フィールドのクリア処理です。
     */
    private void clear() {
        rate = null;
        usageQuantity = null;
    }

    /**
     * レートを計算し、返却します
     *
     * @param csvBean 請求CSV
     * @param ciBean 契約情報
     * @param isUsePublicOnDemand true: 非混合(Unblended)でなく、パブリックオンデマンドを使用, false: 非混合(Unblended)を使用
     */
    private void calc(BillingCsvBean csvBean, ContractInformationBean ciBean, boolean isUsePublicOnDemand) {
        BigDecimal rateForCalc = null;
        BigDecimal costForCalc = null;
        if (isUsePublicOnDemand) {
            rateForCalc = csvBean.getPublicOnDemandRate();
            costForCalc = csvBean.getPublicOnDemandCost();
        } else {
            rateForCalc = csvBean.getUnblendedRate();
            costForCalc = csvBean.getUnblendedCost();
        }

        if (rateForCalc == null) {
            if (costForCalc == null) {
                StringBuilder builder = new StringBuilder();
                builder.append("unblendedRate and unblendedCost is null or ");
                builder.append("publicOnDemandRate and publicOnDemandCost is null (linkedAccountId=");
                builder.append(csvBean.getLinkedAccountId());
                builder.append(",productName=");
                builder.append(csvBean.getProductName());
                builder.append(",usageType=");
                builder.append(csvBean.getUsageType());
                builder.append(")");
                throw new CommonException(builder.toString());
            }

            BigDecimal usageQuantityForCalc = csvBean.getUsageQuantity();
            if (usageQuantityForCalc == null || usageQuantityForCalc.compareTo(BigDecimal.ZERO) == 0) {
                // 使用量が null or 0だった場合、1にして計算する。
                usageQuantity = new BigDecimal("1.0000000000");
                usageQuantityForCalc = usageQuantity;
            }

            // 非混合料金(or パブリックオンデマンドコスト) / 使用量 (小数点第13位で四捨五入)
            BigDecimal rateOfDoller = costForCalc.divide(usageQuantityForCalc, 12, RoundingMode.HALF_UP);

            // レート(ドル) * 為替レート (小数点第13位で四捨五入)
            rate = rateOfDoller.multiply(ciBean.getDollerYen()).setScale(12, RoundingMode.HALF_UP);
        } else {
            // 非混合レート(or パブリックオンデマンドレート) * 為替レート (小数点第13位で四捨五入)
            rate = rateForCalc.multiply(ciBean.getDollerYen()).setScale(12, RoundingMode.HALF_UP);
        }
    }
}
